package com.qingzhu.component.lock.way;

import com.qingzhu.component.lock.annotation.LockCommonBeanAnnotation;
import com.qingzhu.component.lock.common.constant.AutoConfig;
import com.qingzhu.component.lock.common.constant.cache.strategy.LockExpireStrategyTypeConstant;
import com.qingzhu.component.lock.common.entity.LockEntity;
import com.qingzhu.component.lock.common.exception.LockExpiredException;
import com.qingzhu.component.lock.common.generator.LockGenerator;
import com.qingzhu.component.lock.common.pool.ForkLockExecutor;
import com.qingzhu.component.lock.common.util.BaseUtil;
import com.qingzhu.component.lock.starter.config.LockComponentAutoConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;

/**
 * 锁的执行器，当需要更加灵活的使用分布式锁的时候，可以直接只用本类的lock和unlock
 * @author xiangjz
 * @version 1.0
 * @date 2021/1/25 11:14
 */
@ConditionalOnClass(LockComponentAutoConfiguration.class)
@LockCommonBeanAnnotation
@Slf4j
public class LockExecutor {

    private final LockGenerator lockGenerator;

    public LockExecutor(LockGenerator lockGenerator) {
        this.lockGenerator = lockGenerator;
    }

    public LockEntity lock(String key) {
        return lock(key, -1L);
    }

    public LockEntity lock(String key, long expireOnForkingFull) {
        LockEntity lockEntity = lockGenerator.getLock(AutoConfig.COMPONENT_LOCK_KEY_PREFIX + key, expireOnForkingFull);
        lockEntity.lock();
        return lockEntity;
    }

    public void unlock(String key) {
        LockEntity lockEntity = lockGenerator.getLockCache().get(AutoConfig.COMPONENT_LOCK_KEY_PREFIX + key);
        if(!BaseUtil.isEmpty(lockEntity)) {
            lockEntity.unlock();
        }
    }

    public static void doOnExpired(LockEntity lockEntity) {
        /*
         * 如果进入了这里，说明线程被中断(即，业务超时的提醒)
         * 这里直接终止业务操作(自动终止了)
         * 然后unlock锁(finally中会unlock)
         */
        log.error("{} lock[{}] over time, do sth on expired of type [{}] ", AutoConfig.COMPONENT_LOCK_LOG_PREFIX, lockEntity.getLockKey(), lockEntity.getStrategyOnExpired());
        if(LockExpireStrategyTypeConstant.LOCK_EXPIRE_STRATEGY_EXCEPTION == lockEntity.getStrategyOnExpired()) {
            throw new LockExpiredException("lock["+lockEntity.getLockKey()+"] over time");
        } else if(LockExpireStrategyTypeConstant.LOCK_EXPIRE_STRATEGY_IGNORE == lockEntity.getStrategyOnExpired()) {
            // do nothing
        }
    }

}
